#include "wds_gun.h"
#include "wds_bullet_collection.h"
#include "wds_bullet_manager.h"
#include "wds_sounds.h"
#include "wds_lifeobject.h"
#include "wds_bullet_ion.h"

#include "num/rand.h"

#include "dd/dd_man.h"
#include "util/timer.h"
#include "math/facing.h"
#include "util/common_macros.h"

WDSGun::WDSGun(	GUN_TYPES Type, float ExtraAmmoPerc) : m_Type(Type),	
								  m_MagazineCount(7),
								  m_ReloadTime(2.0f),
								  m_CurrentReloadDelay(0.0f),
								  m_BulletIndexOfMagazine(0),
								  m_BulletDelay(1.0f),
								  m_CurrentBulletDelay(0.0f),
								  m_MaxInAccuracy(0),
								  m_InAccuracy(0.05f),
								  m_MinBulletCountAtSingleShot(1),
								  m_MaxBulletCountAtSingleShot(1),
								  m_MinSpeedScale(1.0f),
								  m_MaxSpeedScale(1.0f),
								  m_GunThrustShowing(0),								  
								  m_TimeSinceLastBullet(10.0f)
{
	
	m_SpriteGunThrust.Create("gun_thrust");
	m_SpriteGunThrust.m_Size *= 0.5f;

	switch(m_Type)
	{
		case GUN_TYPE_PISTOL:
			m_MagazineCount = 10;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 1.5f;			
		break;

		case GUN_TYPE_MEAN_MINIGUN:
			m_MagazineCount = 120;
			m_BulletDelay = 14.0f / (float)m_MagazineCount;
			m_ReloadTime = 4.0f;			
			
		break;
		
		case GUN_TYPE_SUBMACHINE:
			m_MagazineCount = 30;
			m_BulletDelay = 3.0f / (float)m_MagazineCount;
			m_ReloadTime = 1.0f;
		break;

		case GUN_TYPE_ASSAULT_RIFLE:
			m_MagazineCount = 25;
			m_BulletDelay = 4.0f / (float)m_MagazineCount;
			m_ReloadTime = 2.0f;
			m_InAccuracy = 7.0f;
		break;

		case GUN_TYPE_SHOTGUN:
			m_MinBulletCountAtSingleShot = 9;
			m_MaxBulletCountAtSingleShot = 12;
			m_MagazineCount = 12;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 2.0f;	
			m_InAccuracy = 7.0f;
			m_MinSpeedScale = 0.5f;
			m_MaxSpeedScale = 1.5f;
		break;

		case GUN_TYPE_SAWED_OFF_SHOTGUN:
			m_MinBulletCountAtSingleShot = 11;
			m_MaxBulletCountAtSingleShot = 11;
			m_MagazineCount = 12;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 2.0f;	
			m_InAccuracy = 15.0f;
		break;

        case GUN_TYPE_JACK_HAMMER:			
			m_MagazineCount = 16;
			m_BulletDelay = 3.5f / (float)m_MagazineCount;
			m_ReloadTime = 2.0f;

			m_MinBulletCountAtSingleShot = 3;
			m_MaxBulletCountAtSingleShot = 4;
			m_InAccuracy = 7.0f;
			m_MinSpeedScale = 0.5f;
			m_MaxSpeedScale = 1.5f;
		break;

		case GUN_TYPE_GAUSS:
			// 6 bullets magazine,  0.5 sec to shoot, 1 sec to reload 
			m_MagazineCount = 6;
			m_BulletDelay = 0.5f;
			m_ReloadTime = 1.0f;

		break;

		case GUN_TYPE_GAUSS_SHOTGUN:
			// 4 bullets magazine, 11 bullets at a time // 1.1 sec to shoot, 2 sec to reload 
			m_MinBulletCountAtSingleShot = 11;
			m_MaxBulletCountAtSingleShot = 11;
			m_MagazineCount = 4;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 2.0f;	
			m_InAccuracy = 7.0f;
			m_MinSpeedScale = 0.5f;
			m_MaxSpeedScale = 1.5f;
		break;
		
		case GUN_TYPE_ION_MINIGUN:
			// 20 bullets magazine, 1 bullets at a time // 3 sec to shoot all// 2 sec to reload
			m_MagazineCount = 20;
			m_BulletDelay = 3.0f / (float)m_MagazineCount;
			m_ReloadTime = 2.0f;	
			m_InAccuracy = 1.0f;
		break;

		case GUN_TYPE_ION_RIFLE:
			// 8 bullets magazine, 1 bullets at a time // 0.5 sec to shoot // 2 sec to reload
			m_MagazineCount = 8;
			m_BulletDelay = 0.5f;
			m_ReloadTime = 2.0f;	
			m_InAccuracy = 7.0f;
		break;

		case GUN_TYPE_ION_SHOTGUN:
			// 10 bullets magazine, 8 bullets at a time // 1 sec to shoot // 2 sec to reload
			m_MagazineCount = 10;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 2.0f;	
			m_MinBulletCountAtSingleShot = 8;
			m_MaxBulletCountAtSingleShot = 8;
			m_InAccuracy = 7.0f;
		break;

		case GUN_TYPE_ION_CANNON:
			// 3 bullets magazine, 1 bullets at a time // 1 sec to shoot // 3 sec to reload
			m_MagazineCount = 3;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 3.0f;				
			m_InAccuracy = 7.0f;
		break;

		case GUN_TYPE_ROCKET_LAUCHER:
			// 5 bullets magazine, 1 bullets at a time // 1 sec to shoot // 1.2 sec to reload
			m_MagazineCount = 5;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 1.2f;

		break;

		case GUN_TYPE_ROCKET_SEEKER:
			// 6 bullets magaine, 1 bullets at a time // 4 sec to shoot, 1 sec to reload
			m_MagazineCount = 6;
			m_BulletDelay = 4.0f / (float)m_MagazineCount;
			m_ReloadTime = 1.0f;

		break;

		case GUN_TYPE_ROCKET_MINIGUN:
			// 16 bullets magaine, 1 bullets at a time // 3 sec to shoot, 1 sec to reload
			m_MagazineCount = 16;
			m_BulletDelay = 3.0f / (float)m_MagazineCount;
			m_ReloadTime = 1.0f;
		break;

		case GUN_TYPE_ROCKET_MINI_SWARMERS:
			// 1 bullet magazine , 5 bullets at a time // 1.5 sec to shoot // 1.5 sec to reload
		break;

		case GUN_TYPE_FLAME_THROWER:
			// 30 bullets magazine // 4 secs to shoot all , 2 sec to reload
			m_MagazineCount = 60;
			m_BulletDelay = m_BulletDelay = 4.0f / (float)m_MagazineCount;
			m_ReloadTime = 2.0f;
		break;

		case GUN_TYPE_PLASMA_MULTI:
			// 8 bullets magazine , 5 bullets at a time, 0.5 sec to shoot , 1 sec to reload
			m_MagazineCount = 8;
			m_BulletDelay = 0.5f;
			m_ReloadTime = 1.0f;

			m_MinBulletCountAtSingleShot = 5;
			m_MaxBulletCountAtSingleShot = 5;			
		break;

		case GUN_TYPE_PLASMA_MINIGUN:
			// 30 bullets magazine , 1 bullet at a time, 4 sec to shoot all, 2 sec to reload
			m_MagazineCount = 30;
			m_BulletDelay = 4.0f / (float)m_MagazineCount;
			m_ReloadTime = 2.0f;
		break;

		case GUN_TYPE_PLASMA_RIFLE:
			// 20 bullets magazine , 1 bullet at a time, 7 sec to shoot all, 1 sec to reload
			m_MagazineCount = 20;
			m_BulletDelay = 7.0f / (float)m_MagazineCount;
			m_ReloadTime = 1.0f;
		break;

		case GUN_TYPE_PLASMA_SHOTGUN:
			// 8 bullets magazine , 15 bullet at a time, 0.5 sec to shoot, 3 sec to reload
			m_MagazineCount = 8;
			m_BulletDelay = 0.5f;
			m_ReloadTime = 3.0f;

			m_MinBulletCountAtSingleShot = 15;
			m_MaxBulletCountAtSingleShot = 15;
			m_MinSpeedScale = 0.75f;
			m_MaxSpeedScale = 1.25f;
		break;

		case GUN_TYPE_PLASMA_CANNON:
			// 3 bullets magazine , 1 bullet at a time, 1 sec to shoot, 2 sec to reload
			m_MagazineCount = 3;
			m_BulletDelay = 1.0f;
			m_ReloadTime = 2.0f;
		break;

		case GUN_TYPE_PULSE:
			// 16 bullets magaine , 2-2.5 secs to shoot	
			m_MagazineCount = 16;
			m_BulletDelay = 2.0f / (float)m_MagazineCount;
			m_ReloadTime = 0.0f;
		break;
	}

	float Temp = (float)m_MagazineCount;
	Temp *= ExtraAmmoPerc;
	m_MagazineCount = (int)Temp;
	m_GunUI.Init();
}


WDSGun::~WDSGun()
{
	
}

void	WDSGun::Tick(float dt)
{
	if(m_TimeSinceLastBullet < 10.0f)
		m_TimeSinceLastBullet += dt;

	if(m_FireBulletDuration > 0)
		m_FireBulletDuration -= dt;

	if(m_GunThrustShowing > 0)
		m_GunThrustShowing -= dt;
	
}
		
void		WDSGun::ShootTick(float dt, const WDSPlayer::Skill& PlayerSkill,  const math::Vec2& Offset)
{
	if(m_CurrentBulletDelay > 0)
	{
		m_CurrentBulletDelay -= ( dt * PlayerSkill.m_FastShot);
		//return;
	}

	if(m_CurrentReloadDelay > 0)
	{
		m_CurrentReloadDelay -= ( dt * PlayerSkill.m_FastLoader);		
	}

		
}

void		WDSGun::Shoot(
						  WDSLifeObject* pShooter,
						  float	SpeedBonus,
						  const math::Vec2& ShootPos,														
						  float Degree,	
						  const math::Vec2& Offset,
						  int SoundSFXIndex
											)
{
	if(m_CurrentReloadDelay > 0)		
		return;
	

	if(m_CurrentBulletDelay > 0)
		return;


	m_CurrentBulletDelay = m_BulletDelay;
	
	m_BulletIndexOfMagazine++;
	if(m_BulletIndexOfMagazine >= m_MagazineCount)
	{
		m_BulletIndexOfMagazine = 0;
		m_CurrentReloadDelay = m_ReloadTime;
		WDSSounds::Instance().PlaySoundSFX(WDSSounds::SFX_GUN_RELOAD);		
	}
	
	ShootPrivate(pShooter, SpeedBonus, ShootPos, Degree, Offset, SoundSFXIndex);	

	m_TimeSinceLastBullet = 0;

	
}

void	WDSGun::ShootPrivate(WDSLifeObject* pShooter,
							float	SpeedBonus,
							const math::Vec2& ShootPos,														
							float Degree,	
							const math::Vec2& Offset,
							int SoundSFXIndex
							)
{
	math::Vec2 TargetDir = math::Facing::VectorFromAngle(Degree); + Offset;	
	//TargetDir.Normalize();
	//TargetDir.y *= GraphicsUtil::W2H;


	float InAcuuracy; 
	WDSBullet* pBullet = NULL;	
	int Count = num::RAND32::Instance().RandInt(m_MinBulletCountAtSingleShot,m_MaxBulletCountAtSingleShot);
	for(int i = 0; i < Count; i++)
	{
		if(  m_MaxBulletCountAtSingleShot == 1 && (m_TimeSinceLastBullet > (m_BulletDelay * 1.5f)) )
			InAcuuracy = 0;		// first one is always accurate.
		else
			InAcuuracy = num::RAND32::Instance().RandFloat(-m_InAccuracy,m_InAccuracy);

		
		pBullet = WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter,ShootPos,Degree + InAcuuracy ,SoundSFXIndex);

		if(pBullet)
		{
			OnBulletCreated(pBullet);
			if(m_MaxBulletCountAtSingleShot > 1)
				pBullet->SetSpeedVariationScale(num::RAND32::Instance().RandFloat(m_MinSpeedScale,m_MaxSpeedScale));

		}
	}
}



WDSBullet::BULLET_TYPES			WDSGun::GetBulletType() const
{
	
	switch(m_Type)
	{
		case GUN_TYPE_PISTOL:
		case GUN_TYPE_MEAN_MINIGUN:
		case GUN_TYPE_SUBMACHINE:
		case GUN_TYPE_ASSAULT_RIFLE:
		case GUN_TYPE_SHOTGUN:
		case GUN_TYPE_SAWED_OFF_SHOTGUN:
		case GUN_TYPE_JACK_HAMMER:		
			if( m_FireBulletDuration > 0)
				return WDSBullet::BULLET_TYPE_FIRE;
			else
				return WDSBullet::BULLET_TYPE_NORMAL;
		break;

		case GUN_TYPE_GAUSS:
		case GUN_TYPE_GAUSS_SHOTGUN:
			if( m_FireBulletDuration > 0)
				return WDSBullet::BULLET_TYPE_FIRE;
			else
				return WDSBullet::BULLET_TYPE_GAUSS;			
		break;

		case GUN_TYPE_ION_MINIGUN:
		case GUN_TYPE_ION_RIFLE:
		case GUN_TYPE_ION_SHOTGUN:
		case GUN_TYPE_ION_CANNON:
			return WDSBullet::BULLET_TYPE_ION;
		break;

		case GUN_TYPE_ROCKET_LAUCHER:		
		case GUN_TYPE_ROCKET_MINIGUN:
		case GUN_TYPE_ROCKET_MINI_SWARMERS:
			return WDSBullet::BULLET_TYPE_ROCKET;
		break;

		case GUN_TYPE_ROCKET_SEEKER:
			return WDSBullet::BULLET_TYPE_HOMINGROCKET;
		break;

		case GUN_TYPE_FLAME_THROWER:
			return WDSBullet::BULLET_TYPE_FLAME;
		break;

		case GUN_TYPE_PLASMA_MULTI:
		case GUN_TYPE_PLASMA_MINIGUN:
		case GUN_TYPE_PLASMA_RIFLE:
		case GUN_TYPE_PLASMA_SHOTGUN:
			return WDSBullet::BULLET_TYPE_PLASMA;
		break;

		case GUN_TYPE_PLASMA_CANNON:
			return WDSBullet::BULLET_TYPE_PLASMA;
		break;

		case GUN_TYPE_PULSE:
			return WDSBullet::BULLET_TYPE_PULSE;
		break;
	}
	

	return WDSBullet::BULLET_TYPE_NORMAL;
}

void		WDSGunPlasmaMulti::ShootPrivate(		WDSLifeObject* pShooter,
													float	SpeedBonus,
													const math::Vec2& ShootPos,														
													float Degree,
													const math::Vec2& Offset,
													int SoundSFXIndex)
{

	
	WDSBullet* pBullet = NULL;
	
	float TempAngle = Degree;
	while(TempAngle > 360.0f)
		TempAngle -= 360.0f;
	while(TempAngle < 0.0f)
		TempAngle += 360.0f;	
	WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter, ShootPos, Degree  , WDSSounds::SFX_NONE);;

	TempAngle = Degree + 12.5f;
	while(TempAngle > 360.0f)
		TempAngle -= 360.0f;
	while(TempAngle < 0.0f)
		TempAngle += 360.0f;	
	pBullet = WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter, ShootPos,   TempAngle  , WDSSounds::SFX_NONE);;


	TempAngle = Degree - 12.5f;
	while(TempAngle > 360.0f)
		TempAngle -= 360.0f;
	while(TempAngle < 0.0f)
		TempAngle += 360.0f;
	WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter, ShootPos,   TempAngle , WDSSounds::SFX_NONE);;

	TempAngle = Degree + 25.0f;
	while(TempAngle > 360.0f)
		TempAngle -= 360.0f;
	while(TempAngle < 0.0f)
		TempAngle += 360.0f;	
	WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter, ShootPos,   TempAngle , WDSSounds::SFX_NONE);;

	TempAngle = Degree - 25.0f;
	while(TempAngle > 360.0f)
		TempAngle -= 360.0f;
	while(TempAngle < 0.0f)
		TempAngle += 360.0f;	
	WDSBulletManager::Instance().Shoot(GetBulletType(), pShooter, ShootPos,   TempAngle  , WDSSounds::SFX_NONE);;
	

}


WDSGunION::WDSGunION(GUN_TYPES Type, float ExtraAmmoPerc,  int CountToBounce) : WDSGun(Type, ExtraAmmoPerc),
														  m_CountToBounce(CountToBounce)
{
}

void	WDSGunION::OnBulletCreated(WDSBullet* pBullet)
{
	WDSGun::OnBulletCreated(pBullet);

	if(!pBullet)
		return;

	((WDSBulletION*)pBullet)->SetCountToBounce(m_CountToBounce);
}

void	WDSGun::Render(const math::Vec2& PlayerPos, const math::Vec2& Offset, float Rotation, float Scale)
{
	if(m_GunThrustShowing > 0)
	{
		m_SpriteGunThrust.m_Pos = PlayerPos - Offset;
		m_SpriteGunThrust.m_Rotation = Rotation;
		m_SpriteGunThrust.m_Color = ColorfRGBA(1.0f,1.0f,1.0f, m_GunThrustShowing * 10.0f);
		//m_SpriteGunThrust.m_Size = 
		m_SpriteGunThrust.RenderRotated();
	}
}

void	WDSGun::OnBulletCreated(WDSBullet* pBullet)
{
	m_GunThrustShowing = 0.1f;
}




